Zach的博客

Swift Collections

Swift Collections

在Swift中自定义一个CollectionType或者是SequenceType类型的数据结构需要实现对应的协议。这里首先给出一个自定义的SequenceType类型的数据结构,然后我们再来逐步分解代码解析之。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
extension Array {
subscript(safe index: Int) -> Element? {
return index>=0 && index<count ? self[index] : nil
}
}

struct ArrayGenerator<T>: GeneratorType {
let array: [T]
var currentIndex = 0

init(_ array:[T]) {
self.array = array
}

mutating func next() -> T? {
return self.array[safe: currentIndex++]
}
}

struct Section<T> : SequenceType {
let title: String
let elements: [T]

subscript(index: Int) -> T? {
return elements[safe: index]
}

func generate() -> ArrayGenerator<T> {
return ArrayGenerator(elements)
}
}

Sequence在Swift中的定义:

A type that can be ietrated in a `for...in` loop

要成为一个Sequence,必须实现SequenceType协议,该协议要求实现一个方法以返回一个Generator,而一个Generator又实现了一个GeneratorType的协议,它封装了我们自定的Sequence类型里面的数据,并且保留了迭代的过程。

在上述代码中,我们首先定一个一个ArrayGenerator作为我们的Generator,然后在我们自定的Section类型中实现generate方法,返回我们的Generator,这样我们就可以在for…in中使用我们的Section了。

1
2
3
4
5
6
7
8
9
10
11
let elements = ["hello", "world", "haha"]
let section = Section(title: "Test", elements: elements)

for e in section {
print(e)
}

var generator = section.generate()
while let e = generator.next() {
print(e)
}

上述代码中,for…in和while循环实际上是一样的。

在实现SequenceType以后,我们还自动获得了许多方法,比如:

1
2
3
section.sort() // ["haha", "hello", "world"]
section.maxElement() // "world"
section.minElement() // "haha"

因为字符串是可比较的,所以上述三个方法均可返回正确的数值。

在讨论SequenceType之后,我们来看看CollectionType的定义:

1
2
3
protocol CollectionType: Indexable, SequenceType {

}

所以要定义一个CollectionType类型的数据结构,只要我们的Section再实现Indexable协议即可。我们改写Section的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct Section<T> : CollectionType {
let title: String
let elements: [T]

subscript(safe index: Int) -> T? {
return index>=0 && index<elements.count ? elements[index] : nil
}

func generate() -> ArrayGenerator<T> {
return ArrayGenerator(elements)
}

var startIndex: Int {
return 0
}

var endIndex: Int {
return elements.count
}

subscript(index: Int) -> T {
return elements[index]
}

}

由于遵守Indexable协议的类型必须实现一个下标脚本,其返回值是一个non-optinal的值,所以我们需要改写Section为上述形式。

至此我们就创建了一个CollectionType类型的Section。

在这里需要额外说一点:在我们用下标语法来取值的时候,swift中的array和dictionary的行为是不一致的,举个栗子:

1
2
3
4
5
let arr = ["h", "e", "l"];
let dict = ["hello":1, "world":2]

dict["haha"] // nil
arr[3] // error

字典和数组都是实现了CollectionType协议,换言之,它们都实现了返回一个non-optional的下表脚本,那为什么上述代码中字典返回nil呢?我们看字典的下表语法:

1
2
3
4
struct Dictionary<Key : Hashable, Value> {
subscript(key: Key) -> Value?
subscript(position: DictionaryIndex<Key, Value>) -> (Key, Value)
}

实际上在字典中返回non-optional的是第二个脚本。所以当我们按照以下方法来操作字典时就会报错。

1
2
var startIndex = dict.startIndex
dict[startIndex.advancedBy(3)] // error

Indexable要求返回一个non-optional的值仅仅为了性能,毕竟直接crash比检查一个index的有效性快。